package com.coderly.onlinegallerysharingplatform.toolkit;

import com.coderly.onlinegallerysharingplatform.common.convention.errorcode.ErrorCode;
import com.coderly.onlinegallerysharingplatform.common.convention.exception.BizException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * Redis工具类，封装重试逻辑
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class RedisUtils {

    private final RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 默认最大重试次数
     */
    private static final int DEFAULT_MAX_RETRIES = 3;
    
    /**
     * 默认重试等待时间基数（毫秒）
     */
    private static final long DEFAULT_RETRY_WAIT_BASE = 100L;

    /**
     * 执行Redis操作，带重试机制
     *
     * @param operation Redis操作
     * @param errorMsg 错误信息
     * @param <T> 返回类型
     * @return 操作结果
     */
    public <T> T executeWithRetry(Supplier<T> operation, String errorMsg) {
        return executeWithRetry(operation, errorMsg, DEFAULT_MAX_RETRIES, null);
    }

    /**
     * 执行Redis操作，带重试机制
     *
     * @param operation Redis操作
     * @param errorMsg 错误信息
     * @param maxRetries 最大重试次数
     * @param context 上下文信息，用于日志
     * @param <T> 返回类型
     * @return 操作结果
     */
    public <T> T executeWithRetry(Supplier<T> operation, String errorMsg, int maxRetries, String context) {
        int retryCount = 0;
        Exception lastException = null;

        while (retryCount < maxRetries) {
            try {
                return operation.get();
            } catch (Exception e) {
                lastException = e;
                retryCount++;
                
                if (context != null) {
                    log.warn("Redis操作失败，正在进行第{}次重试。上下文: {}", retryCount, context, e);
                } else {
                    log.warn("Redis操作失败，正在进行第{}次重试", retryCount, e);
                }
                
                if (retryCount >= maxRetries) {
                    break;
                }
                
                try {
                    // 重试前等待一小段时间，时间随重试次数增加
                    Thread.sleep(DEFAULT_RETRY_WAIT_BASE * retryCount);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("线程被中断", ie);
                }
            }
        }
        
        // 所有重试都失败
        if (context != null) {
            log.error("Redis操作最终失败。上下文: {}", context, lastException);
        } else {
            log.error("Redis操作最终失败", lastException);
        }
        
        throw new BizException(ErrorCode.SYSTEM_ERROR, errorMsg);
    }

    /**
     * 执行Redis操作，带重试机制，无返回值
     *
     * @param operation Redis操作
     * @param errorMsg 错误信息
     */
    public void executeWithRetryNoReturn(Runnable operation, String errorMsg) {
        executeWithRetryNoReturn(operation, errorMsg, DEFAULT_MAX_RETRIES, null);
    }

    /**
     * 执行Redis操作，带重试机制，无返回值
     *
     * @param operation Redis操作
     * @param errorMsg 错误信息
     * @param maxRetries 最大重试次数
     * @param context 上下文信息，用于日志
     */
    public void executeWithRetryNoReturn(Runnable operation, String errorMsg, int maxRetries, String context) {
        int retryCount = 0;
        Exception lastException = null;

        while (retryCount < maxRetries) {
            try {
                operation.run();
                return;
            } catch (Exception e) {
                lastException = e;
                retryCount++;
                
                if (context != null) {
                    log.warn("Redis操作失败，正在进行第{}次重试。上下文: {}", retryCount, context, e);
                } else {
                    log.warn("Redis操作失败，正在进行第{}次重试", retryCount, e);
                }
                
                if (retryCount >= maxRetries) {
                    break;
                }
                
                try {
                    Thread.sleep(DEFAULT_RETRY_WAIT_BASE * retryCount);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("线程被中断", ie);
                }
            }
        }
        
        // 所有重试都失败
        if (context != null) {
            log.error("Redis操作最终失败。上下文: {}", context, lastException);
        } else {
            log.error("Redis操作最终失败", lastException);
        }
        
        throw new BizException(ErrorCode.SYSTEM_ERROR, errorMsg);
    }
    
    /**
     * 执行Redis操作，带重试机制，忽略异常
     *
     * @param operation Redis操作
     * @param context 上下文信息，用于日志
     */
    public void executeWithRetryIgnoreException(Runnable operation, String context) {
        int retryCount = 0;

        while (retryCount < DEFAULT_MAX_RETRIES) {
            try {
                operation.run();
                return;
            } catch (Exception e) {
                retryCount++;
                log.warn("Redis操作失败，正在进行第{}次重试。上下文: {}", retryCount, context, e);
                
                if (retryCount >= DEFAULT_MAX_RETRIES) {
                    log.error("Redis操作最终失败，但将忽略此异常。上下文: {}", context, e);
                    return;
                }
                
                try {
                    Thread.sleep(DEFAULT_RETRY_WAIT_BASE * retryCount);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    return;
                }
            }
        }
    }
    
    /**
     * 检查键是否存在
     *
     * @param key 键
     * @return 是否存在
     */
    public Boolean hasKey(String key) {
        return executeWithRetry(() -> redisTemplate.hasKey(key), "检查键是否存在失败", DEFAULT_MAX_RETRIES, "key: " + key);
    }
    
    /**
     * 获取值
     *
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return executeWithRetry(() -> redisTemplate.opsForValue().get(key), "获取值失败", DEFAULT_MAX_RETRIES, "key: " + key);
    }
    
    /**
     * 设置值
     *
     * @param key 键
     * @param value 值
     * @param timeout 超时时间
     * @param unit 时间单位
     */
    public void set(String key, Object value, long timeout, TimeUnit unit) {
        executeWithRetryNoReturn(() -> redisTemplate.opsForValue().set(key, value, timeout, unit), 
                "设置值失败", DEFAULT_MAX_RETRIES, "key: " + key);
    }
    
    /**
     * 删除键
     *
     * @param key 键
     */
    public void delete(String key) {
        executeWithRetryIgnoreException(() -> redisTemplate.delete(key), "key: " + key);
    }
    
    /**
     * 自增
     *
     * @param key 键
     * @return 自增后的值
     */
    public Long increment(String key) {
        return executeWithRetry(() -> redisTemplate.opsForValue().increment(key), 
                "自增操作失败", DEFAULT_MAX_RETRIES, "key: " + key);
    }
} 